﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace nsadv.Statistic.Services.PList
{
	public class ApplePListArray: IEnumerable<object>
	{
		protected readonly List<object> Items = new List<object>();

		public ApplePListArray(XElement element)
		{
			Xml = element;
			foreach (var child in element.Elements())
			{
				Items.Add(ApplePList.CreatePListObject(child));
			}
		}

		public int Count { get { return Items.Count; } }

		public object this[int index] { get { return Items[index]; } }

		public XElement Xml { get; private set; }

		public IEnumerator<object> GetEnumerator()
		{
			return Items.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return (Items as IEnumerable).GetEnumerator();
		}
	}

	public class ApplePListDict : IEnumerable<object>
	{
		protected readonly Dictionary<string, object> Items = new Dictionary<string, object>();
		protected readonly Dictionary<string, string> StrValues = new Dictionary<string, string>();

		public ApplePListDict(XElement element)
		{
			Xml = element;
			var keys = element.Elements().Where(x => x.Name.LocalName == "key");
			foreach (var key in keys)
			{
				var value = key.ElementsAfterSelf().FirstOrDefault();
				StrValues.Add(key.Value, value.Value);
				Items.Add(key.Value, ApplePList.CreatePListObject(value));
			}
		}

		public object this[string key] 
		{ 
			get 
			{
				if (Items.ContainsKey(key))
					return Items[key];
				else
					return null;
			} 
		}

		public string GetStringValue(string key)
		{
			return StrValues[key];
		}

		public XElement Xml { get; private set; }

		public IEnumerator<object> GetEnumerator()
		{
			return Items.Values.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}
	}

	public class ApplePList
	{
		internal static object CreatePListObject(XElement element)
		{
			if (element == null)
				return null;

			switch (element.Name.LocalName)
			{
				case "string":
					return element.Value;
				case "real":
					return (Double) element;
				case "integer":
					return (int) element;
				case "date":
					return (DateTime) element;
				case "true":
					return true;
				case "false":
					return false;
				case "array":
					return new ApplePListArray(element);
				case "dict":
					return new ApplePListDict(element);
				default:
					return null;
			}
		}

		public ApplePListDict Root { get; private set; }

		public XDocument Xml { get; private set; }

		protected void Parse(XDocument document)
		{
			Xml = document;
			var dict = document
					.Root
					.Elements()
					.FirstOrDefault(x => x.Name.LocalName == "dict");

			Root = CreatePListObject(dict) as ApplePListDict;			
		}

		public ApplePList(XDocument document)
		{
			Parse(document);
		}

		public ApplePList(string document)
		{
			var xml = XDocument.Parse(document);
			Parse(xml);
		}
	}
}